package fr.labri.harmony.analysis.jsep.results.jung;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.ImageCacher.Embedded;
import org.apache.batik.svggen.SVGGraphics2D;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ChainedTransformer;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
import edu.uci.ics.jung.algorithms.layout.BalloonLayout;
import edu.uci.ics.jung.algorithms.layout.CircleLayout;
import edu.uci.ics.jung.algorithms.layout.DAGLayout;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
import edu.uci.ics.jung.algorithms.layout.KKLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
import edu.uci.ics.jung.algorithms.layout.SpringLayout;
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
import edu.uci.ics.jung.graph.DelegateForest;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.graph.event.GraphEvent.Vertex;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationImageServer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import fr.labri.harmony.analysis.jsep.results.LoadRules;
import fr.labri.harmony.analysis.jsep.results.Pair;



public class DigraphRecommendation {


	static Map<String,Integer> vertices = new HashMap<String,Integer>();

	static Map<String,Integer> in_out = new HashMap<String,Integer>();
	//vertexId - Ingoing score 
	static Map<Integer,Integer> score = new HashMap<Integer, Integer>();
	static Map<Integer,Integer> score_groups = new HashMap<Integer, Integer>();

	static Map<String,String> edges_labels = new HashMap<String, String>();

	static Map<String,Set<String>> deps = new HashMap<String, Set<String>>();

	static Map<Integer,Set<Integer>> clusters = new HashMap<Integer, Set<Integer>>();

	static Map<String,Integer> usage = new HashMap<String, Integer>();

	public static void main(String[] args) throws NumberFormatException, IOException, ParserConfigurationException {

		//		for(Set<String> cat : LoadRules.getCategories()) {
		//			if(!cat.contains("gson"))
		//				continue;
		Map<String,Map<String,Integer>> migrations = new HashMap<String, Map<String,Integer>>();
//		for(String source : cat) 
//			for(String target : cat) {
//				if(source.equals(target)==false) {
//					if(LoadRules.getScore(source, target)==null)
//						continue;
//					int score = LoadRules.getScore(source, target);
//					if(score > 0) {
//						if(!migrations.containsKey(source)) migrations.put(source,new HashMap<String, Integer>());
//						migrations.get(source).put(target, score);
//					}
//				}
//			}

		migrations.put("X", new HashMap<String, Integer>());
		migrations.put("Y", new HashMap<String, Integer>());
		migrations.put("Z", new HashMap<String, Integer>());
		migrations.put("W", new HashMap<String, Integer>());
		migrations.get("X").put("Z", 2);
		migrations.get("Y").put("X", 4);
		migrations.get("Z").put("Y", 2);
		migrations.get("Z").put("X", 1);
		migrations.get("W").put("Z", 2);
		migrations.get("Z").put("W", 1);

		int cpt = 0;
		for(String elt : migrations.keySet()) {
			if(!vertices.containsKey(elt)){
				vertices.put(elt, cpt);
				cpt++;
			}
			for(String tgt : migrations.get(elt).keySet()) {
				if(!vertices.containsKey(tgt)){
					vertices.put(tgt, cpt);
					cpt++;
				}
			}
		}

		for(String elt : vertices.keySet()) {
			int out = 0;
			int in = 0;
			System.out.println(elt);
			if(migrations.containsKey(elt)) {
				for(int score_out : migrations.get(elt).values()) {
					out = out+score_out;
					System.out.println(score_out);
				}
			}
			for(String src : migrations.keySet()) {
				if(!src.equals(elt)) {
					if(migrations.get(src).get(elt)!=null){
						in = in+migrations.get(src).get(elt);
						System.out.println(src+"\t"+migrations.get(src).get(elt));

					}
				}
			}
			int total = in-out;
			System.out.println("Total "+total);
			in_out.put(elt, in-out);
			System.out.println("***");
		}




		// Graph<V, E> where V is the type of the vertices
		// and E is the type of the edges
		//DirectedGraph<Integer, String> g = new DirectedSparseMultigraph<Integer, String>();
		Forest<Integer, String> g = new DelegateForest<Integer, String>();


		for(String vertex : vertices.keySet()) {
			if(deps.containsKey(vertex) || deps.values().contains(vertex))
				g.addVertex((Integer)vertices.get(vertex));
		}

		int j = 0;
		for(String elt_src : migrations.keySet()) {
			int src = vertices.get(elt_src);
			for(String elt_tgt : migrations.get(elt_src).keySet()) {
				int tgt = vertices.get(elt_tgt);
				g.addEdge("Edge-"+j,src,tgt);
				System.out.println("Edge-"+j+" "+elt_src+" "+elt_tgt+" "+Integer.toString(migrations.get(elt_src).get(elt_tgt)));
				edges_labels.put("Edge-"+j,	Integer.toString(migrations.get(elt_src).get(elt_tgt)));
				j++;
				if(!score.containsKey(tgt)) score.put(tgt,0);
				//score.put(tgt,score.get(tgt)+switchings.get(elt_src).get(elt_tgt).getFirst());
				score.put(tgt,score.get(tgt)+migrations.get(elt_src).get(elt_tgt));
				if(!score_groups.containsKey(tgt)) score_groups.put(tgt,0);
				score_groups.put(tgt,score_groups.get(tgt)+migrations.get(elt_src).get(elt_tgt));

				if(!clusters.containsKey(src)) clusters.put(src, new HashSet<Integer>());
				if(!clusters.containsKey(tgt)) clusters.put(tgt, new HashSet<Integer>());
				clusters.get(src).add(tgt);
				clusters.get(tgt).add(src);

			}

		}

		//	Layout<Integer, String> layout = new FRLayout<Integer, String> (g);
		//Layout<Integer, String> layout = new ISOMLayout<Integer, String> (g);
		Layout<Integer, String> layout = new KKLayout<Integer, String> (g);
		//	Layout<Integer, String> layout = new SpringLayout<Integer, String> (g);
		//	Layout<Integer, String> layout = new RadialTreeLayout<Integer, String> (g);
		//	Layout<Integer, String> layout = new DAGLayout<Integer, String> (g);
		//Layout<Integer, String> layout = new TreeLayout<Integer, String> (g);
		//Layout<Integer, String> layout = new BalloonLayout<Integer, String>(g);

		//layout.setSize(new Dimension(900,900)); // sets the initial size of the space
		VisualizationViewer<Integer,String> vv =
				new VisualizationViewer<Integer, String>(layout, new Dimension(950, 950)); //Sets the viewing area size

		Transformer<Integer,Shape> vertexSize = new Transformer<Integer,Shape>(){
			public Shape transform(Integer i){
				for(String vertex : vertices.keySet()) {
					if(vertices.get(vertex)==i) {
						int value = in_out.get(vertex);
						RectangularShape shape;
						int min = Integer.MAX_VALUE;
						int max = Integer.MIN_VALUE;
						for(int score  : in_out.values()){
							min = Math.min(min,score);
							max = Math.max(max,score);
						}
						int dim1= -13;
						int dim2= 26;
						if(value>0) {
							int ratio = value*15/max;
							dim1-=ratio;
							dim2+=ratio;
							shape = new Ellipse2D.Double(dim1, dim1, dim2, dim2);
						}
						else if(value<0) {
							int ratio = value*10/min;
							dim1-=ratio;
							dim2+=ratio;
							shape = new Rectangle2D.Double(dim1, dim1, dim2, dim2);
						}
						else 
							shape = new RoundRectangle2D.Double(dim1, dim1, dim2, dim2, 18.0, 18.0);
						// in this case, the vertex is twice as large
						double base = 1.0;
						double scale = 0.05;
						//				if(score.containsKey(i)) {
						//					if(score.get(i)==0)
						//						scale = base;
						//					else
						//						scale = base + (scale*(double)score.get(i)/10);
						//				}
						//				else
						scale = base;

						return AffineTransform.getScaleInstance(scale, scale).createTransformedShape(shape);
					}
				}
				return null;
			}
		};
		Transformer<Integer,Paint> vertexColor = new Transformer<Integer,Paint>() {
			public Paint transform(Integer i) {
				for(String vertex : vertices.keySet()) {
					if(vertices.get(vertex)==i) {
						int value = in_out.get(vertex);
						int min = Integer.MAX_VALUE;
						int max = Integer.MIN_VALUE;
						for(int score  : in_out.values()){
							min = Math.min(min,score);
							max = Math.max(max,score);
						}
						//							if(min==value) return new Color(249,22,22);
						//							if(max==value) return new Color(0,179,24);

						if(value < 0) {
							int ratio = value*255/min;
							return new Color(255,255-ratio,255-ratio);
						}
						if(value > 0)  {
							int ratio = value*255/max;
							return new Color(255-Math.abs(ratio),255,255-Math.abs(ratio));
						}
					}
				}
				return new Color(255,255,255);
			}
		};

		Transformer<Integer, String> vertexLabel = new Transformer<Integer, String>() {
			public String transform(Integer i) {
				for(String vertex : vertices.keySet()) {
					if(vertices.get(vertex)==i)
						return vertex+" ("+in_out.get(vertex)+")";
				}
				return "";
			}
		};

		Transformer<String, String> edgeLabel = new Transformer<String, String>() {
			public String transform(String edge) {
				if(edge.startsWith("Edge"))
					if(Integer.valueOf(edges_labels.get(edge))>0)
						return edges_labels.get(edge);
					else
						return "";
				else
					return "";
			}
		};

		Transformer<String, Stroke> edgeStroke = new Transformer<String, Stroke>() {
			public Stroke transform(String edge) {

				float dash[] = {1.0f};
				//System.out.println(edge+"\t"+edges_labels.get(edge));
				float score = Float.parseFloat(edges_labels.get(edge));
				Stroke edgeStroke = new BasicStroke(0.12f*score, BasicStroke.CAP_ROUND,
						BasicStroke.CAP_ROUND, 1.0f, dash, 0.0f);
				return edgeStroke;		

			}
		};

		Transformer<String, Font> edgeFont = new Transformer<String, Font>() {
			public Font transform(String edge) {
				int score = Integer.valueOf(edges_labels.get(edge))/2;
				Font edgeFont = new Font("Helvetica", Font.PLAIN, 12);//+(1*score));
				return edgeFont;			
			}
		};

		Transformer<Integer, Font> vertexFont = new Transformer<Integer, Font>() {
			public Font transform(Integer i) {
				Font edgeFont = new Font("Helvetica", Font.BOLD, 12);
				//				if(score.containsKey(i)) {
				//					if(score.get(i) > 0) {
				//						int size = score.get(i)/4;
				//						edgeFont = new Font("Helvetica", Font.PLAIN, 16+size);
				//					}
				//				}
				return edgeFont;
			}
		};

		vv.getRenderContext().setVertexFillPaintTransformer(vertexColor);
		vv.getRenderContext().setVertexShapeTransformer(vertexSize);
		vv.getRenderContext().setVertexLabelTransformer(vertexLabel);
		vv.getRenderContext().setVertexFontTransformer(vertexFont);
		vv.getRenderContext().setEdgeLabelTransformer(edgeLabel);
		vv.getRenderContext().setEdgeStrokeTransformer(edgeStroke);
		vv.getRenderContext().setEdgeArrowStrokeTransformer(edgeStroke);
		vv.getRenderContext().setEdgeFontTransformer(edgeFont);
		vv.setBackground(Color.WHITE);

		vv.getRenderer().getVertexLabelRenderer().setPosition(Position.AUTO);

		DefaultModalGraphMouse gm = new DefaultModalGraphMouse();
		gm.setMode(ModalGraphMouse.Mode.TRANSFORMING);
		gm.setMode(ModalGraphMouse.Mode.EDITING);
		gm.setMode(ModalGraphMouse.Mode.PICKING);
		vv.setGraphMouse(gm);

		//vv.set

		//Capture image
		final DocumentBuilderFactory docBuilderFactory =
				DocumentBuilderFactory
				.newInstance();
		final DocumentBuilder docBuilder =
				docBuilderFactory.newDocumentBuilder();
		final Document document = docBuilder.newDocument();
		final Element svgelem = document.createElement("svg");
		document.appendChild(svgelem);


		SVGGraphics2D graphic2d = new SVGGraphics2D(document);

		vv.printAll(graphic2d);
		vv.addKeyListener(new MyListener(vv));

		JFrame frame = new JFrame("Simple Graph View");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(vv); 

		frame.pack();
		frame.setVisible(true);

		//break;
		//}
	}

	private static class MyListener implements KeyListener {

		private VisualizationViewer<?,?> vv;

		public MyListener(VisualizationViewer<?,?> vv) {
			this.vv = vv;
		}

		@Override
		public void keyTyped(KeyEvent e) {
			System.out.println("Generating SVG");
			DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
			String svgNS = "http://www.w3.org/2000/svg";
			Document doc = domImpl.createDocument(svgNS, "svg", null);
			SVGGraphics2D gr = new SVGGraphics2D(doc);
			vv.paint(gr);
			try {
				gr.stream(new FileWriter("/tmp/jung/"+String.valueOf(Math.random())+".svg"));
			} catch (IOException e1) {

				e1.printStackTrace();
			}
		}

		@Override
		public void keyPressed(KeyEvent e) {

		}

		@Override
		public void keyReleased(KeyEvent e) {
			// TODO Auto-generated method stub

		}

	}

}
